home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.004 / xemacs-1 / xemacs-19.13 / src / nas.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-18  |  15.5 KB  |  735 lines

  1. /* nas.c --- XEmacs support for the Network Audio System server.
  2.  *
  3.  * Author: Richard Caley <R.Caley@ed.ac.uk>
  4.  *
  5.  * Copyright 1994 Free Software Foundation, Inc.
  6.  * Copyright 1993 Network Computing Devices, Inc.
  7.  *
  8.  * Permission to use, copy, modify, distribute, and sell this software and
  9.  * its documentation for any purpose is hereby granted without fee, provided
  10.  * that the above copyright notice appear in all copies and that both that
  11.  * copyright notice and this permission notice appear in supporting
  12.  * documentation, and that the name Network Computing Devices, Inc. not be
  13.  * used in advertising or publicity pertaining to distribution of this 
  14.  * software without specific, written prior permission.
  15.  * 
  16.  * THIS SOFTWARE IS PROVIDED 'AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
  17.  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
  18.  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  19.  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
  20.  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
  21.  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
  22.  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
  23.  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
  24.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  25.  */
  26.  
  27. /* Synched up with: Not in FSF. */
  28.  
  29. /* There are four compile-time options.
  30.  *
  31.  * XTOOLKIT    This will be part of an Xt program.
  32.  * 
  33.  * XTEVENTS    The playing will be supervised asynchronously by the Xt event
  34.  *        loop.  If not set, playing will be completed within the call
  35.  *        to play_file etc. 
  36.  *
  37.  * ROBUST_PLAY    Causes errors in nas to be caught.  This means that the
  38.  *        program will attempt not to die if the nas server does.
  39.  *
  40.  * CACHE_SOUNDS    Causes the sounds to be played in buckets in the NAS
  41.  *        server.  They are named by their comment field, or if that is
  42.  *        empty by the filename, or for play_sound_data by a name made up
  43.  *        from the sample itself.
  44.  */
  45.  
  46. /* CHANGES:
  47.  *    10/8/94, rjc    Changed names from netaudio to nas
  48.  *            Added back asynchronous play if nas library has
  49.  *            correct error facilities.
  50.  *      4/11/94, rjc    Added wait_for_sounds to be called when user wants to
  51.  *            be sure all play has finished.
  52.  */
  53.  
  54. #if __STDC__ || defined (STDC_HEADERS)
  55.  
  56. #    include <stdlib.h>
  57. #    include <unistd.h>
  58. #    include <stdarg.h>
  59.  
  60. #endif
  61.  
  62. #include <stdio.h>
  63. #include <string.h>
  64. #include <config.h> /* for CONST in syssignal.h (neal@ctd.comsat.com) */
  65. #include "syssignal.h"
  66.  
  67. #include <audio/audiolib.h>
  68. #include <audio/soundlib.h>
  69. #include <audio/snd.h>
  70. #include <audio/fileutil.h>
  71.  
  72. #ifdef emacs
  73.  
  74. #    include <config.h>
  75. #    include "lisp.h"
  76.  
  77. #    define XTOOLKIT
  78. #    define XTEVENTS
  79. #    define ROBUST_PLAY
  80. #    define CACHE_SOUNDS
  81.  
  82.     /*
  83.      * For old NAS libraries, force playing to be synchronous
  84.      * and declare the long jump point locally.
  85.      */
  86.  
  87. #    if defined (NAS_NO_ERROR_JUMP)
  88.  
  89. #    undef XTEVENTS
  90.  
  91. #    include <setjmp.h>
  92.     jmp_buf AuXtErrorJump;
  93. #    endif
  94.  
  95.      /* The GETTEXT is correct. --ben */
  96. #    define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str))
  97.  
  98. #    define play_sound_file nas_play_sound_file
  99. #    define play_sound_data nas_play_sound_data
  100. #    define wait_for_sounds nas_wait_for_sounds
  101. #    define init_play       nas_init_play
  102. #    define close_down_play nas_close_down_play
  103.  
  104. #else /* !emacs */
  105. #    define warn(str) fprintf (stderr, "%s\n", (str))
  106. #    define CONST const
  107. #endif /* emacs */
  108.  
  109. #ifdef XTOOLKIT
  110. #    include <X11/Intrinsic.h>
  111. #    include <audio/Xtutil.h>
  112. #endif
  113.  
  114. #if defined (ROBUST_PLAY)
  115. static AuBool CatchIoErrorAndJump (AuServer *aud);
  116. static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event);
  117. SIGTYPE sigpipe_handle (int signo);
  118. #endif
  119.  
  120. extern Lisp_Object Vsynchronous_sounds;
  121.  
  122. static Sound SoundOpenDataForReading (unsigned char *data, int length);
  123.  
  124. static AuServer       *aud;
  125.  
  126. /* count of sounds currently being played. */
  127. static int sounds_in_play;
  128.  
  129.  
  130. #ifdef XTOOLKIT
  131. static Display *aud_server;
  132. static XtInputId input_id;
  133. #else
  134. static char *aud_server;
  135. #endif /* XTOOLKIT */
  136.  
  137. char *
  138. init_play (
  139. #ifdef XTOOLKIT
  140.        Display *display
  141. #else
  142.        char *server
  143. #endif
  144.        )
  145. {
  146.   char *err_message;
  147.   SIGTYPE (*old_sigpipe) ();
  148.  
  149. #ifdef XTOOLKIT
  150.   char * server = DisplayString (display);
  151.   XtAppContext app_context = XtDisplayToApplicationContext (display);
  152.  
  153.   aud_server = display;
  154. #else
  155.  
  156.   aud_server = server;
  157. #endif
  158.  
  159. #ifdef ROBUST_PLAY
  160.   old_sigpipe = signal (SIGPIPE, sigpipe_handle);
  161.   if (setjmp (AuXtErrorJump))
  162.     {
  163.       signal (SIGPIPE, old_sigpipe);
  164. #ifdef emacs
  165.       start_interrupts ();
  166. #endif  
  167.       return "error in NAS";
  168.     }
  169. #endif
  170.  
  171. #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP)
  172.   AuDefaultIOErrorHandler = CatchIoErrorAndJump;
  173.   AuDefaultErrorHandler = CatchErrorAndJump;
  174. #endif
  175.  
  176. #ifdef emacs
  177.   stop_interrupts ();
  178. #endif  
  179.   aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message);
  180. #ifdef emacs
  181.   start_interrupts ();
  182. #endif  
  183.   if (!aud)
  184.     {
  185. #ifdef ROBUST_PLAY
  186.       signal (SIGPIPE, old_sigpipe);
  187. #endif
  188.       if (err_message == NULL)
  189.     return "Can't connect to audio server";
  190.       else
  191.     return err_message;
  192.     }
  193.  
  194. #if defined (ROBUST_PLAY)
  195. # if defined (NAS_NO_ERROR_JUMP)
  196.   aud->funcs.ioerror_handler = CatchIoErrorAndJump;
  197.   aud->funcs.error_handler = CatchErrorAndJump;
  198. # else /* !NAS_NO_ERROR_JUMP */
  199.   AuDefaultIOErrorHandler = NULL;
  200.   AuDefaultErrorHandler = NULL;
  201. # endif
  202. #endif
  203.  
  204. #ifdef XTEVENTS
  205.   input_id = AuXtAppAddAudioHandler (app_context, aud); 
  206. #endif
  207.  
  208. #ifdef CACHE_SOUNDS
  209.   AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL);
  210. #endif
  211.  
  212. #ifdef ROBUST_PLAY
  213.   signal (SIGPIPE, old_sigpipe);
  214. #endif
  215.  
  216.   sounds_in_play = 0;
  217.  
  218.   return NULL;
  219. }
  220.  
  221. void
  222. close_down_play (void)
  223.  
  224. {
  225.   AuCloseServer (aud);
  226.   warn ("disconnected from audio server");
  227. }
  228.  
  229.  /********************************************************************\
  230.  *                                                                    *
  231.  * Callback which is run when the sound finishes playing.             *
  232.  *                                                                    *
  233.  \********************************************************************/
  234.  
  235. static void
  236. doneCB (AuServer       *aud,
  237.     AuEventHandlerRec *handler,
  238.     AuEvent        *ev,
  239.     AuPointer       data)
  240. {
  241.   int         *in_play_p = (int *) data;
  242.  
  243.   (*in_play_p) --;
  244. }
  245.  
  246. #ifdef CACHE_SOUNDS
  247.  
  248.  /********************************************************************\
  249.  *                                                                    *
  250.  * Play a sound by playing the relevant bucket, if any or             *
  251.  * downloading it if not.                                             *
  252.  *                                                                    *
  253.  \********************************************************************/
  254.  
  255. static void
  256. do_caching_play (Sound s,
  257.          int volume,
  258.          unsigned char *buf)
  259.  
  260. {
  261.   AuBucketAttributes *list, b;
  262.   AuBucketID      id;
  263.   int n;
  264.  
  265.   AuSetString (AuBucketDescription (&b),
  266.            AuStringLatin1, strlen (SoundComment (s)), SoundComment (s));
  267.  
  268.   list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL);
  269.  
  270.   if (list == NULL)
  271.     {
  272.       unsigned char *my_buf;
  273.  
  274.       if (buf==NULL)
  275.     {
  276.       if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
  277.         {
  278.           return;
  279.         }
  280.  
  281.       if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
  282.         {
  283.           free (my_buf);
  284.           return;
  285.         }
  286.     }
  287.       else
  288.     my_buf=buf;
  289.  
  290.       id = AuSoundCreateBucketFromData (aud, 
  291.                     s,
  292.                     my_buf,
  293.                     AuAccessAllMasks, 
  294.                     NULL,
  295.                     NULL);
  296.       if (buf == NULL)
  297.     free (my_buf);
  298.     }
  299.   else /* found cached sound */
  300.     {
  301.       id = AuBucketIdentifier (list);
  302.       AuFreeBucketAttributes (aud, n, list);
  303.     }
  304.  
  305.   sounds_in_play++;
  306.  
  307.   AuSoundPlayFromBucket (aud, 
  308.              id, 
  309.              AuNone,
  310.              AuFixedPointFromFraction (volume, 100), 
  311.              doneCB, (AuPointer) &sounds_in_play,
  312.              1,
  313.              NULL, NULL,
  314.              NULL, NULL);
  315.  
  316. }
  317. #endif /* CACHE_SOUNDS */
  318.  
  319.  
  320. void 
  321. wait_for_sounds (void)
  322.  
  323. {
  324.   AuEvent         ev;
  325.  
  326.   while (sounds_in_play>0)
  327.     {
  328.       AuNextEvent (aud, AuTrue, &ev);
  329.       AuDispatchEvent (aud, &ev);
  330.     }
  331. }
  332.  
  333. int
  334. play_sound_file (char *sound_file,
  335.          int volume)
  336. {
  337.   SIGTYPE (*old_sigpipe) ();
  338.  
  339. #ifdef ROBUST_PLAY
  340.   old_sigpipe=signal (SIGPIPE, sigpipe_handle);
  341.   if (setjmp (AuXtErrorJump))
  342.     {
  343.       signal (SIGPIPE, old_sigpipe);
  344.       return 0;
  345.     }
  346. #endif
  347.  
  348.   if (aud==NULL)
  349.     if (aud_server != NULL)
  350.       {
  351.     char *m;
  352.     /* attempt to reconect */
  353.     if ((m=init_play (aud_server))!= NULL)
  354.       {
  355.  
  356. #ifdef ROBUST_PLAY
  357.         signal (SIGPIPE, old_sigpipe);
  358. #endif
  359.         return 0;
  360.       }
  361.       }
  362.     else
  363.       {
  364.     warn ("Attempt to play with no audio init\n");
  365. #ifdef ROBUST_PLAY
  366.     signal (SIGPIPE, old_sigpipe);
  367. #endif
  368.     return 0;
  369.       }
  370.  
  371. #ifndef CACHE_SOUNDS
  372.   sounds_in_play++;
  373.   AuSoundPlayFromFile (aud,
  374.                sound_file,
  375.                AuNone,
  376.                AuFixedPointFromFraction (volume,100),
  377.                doneCB, (AuPointer) &sounds_in_play,
  378.                NULL,
  379.                NULL,
  380.                NULL,
  381.                NULL);
  382. #else
  383.   /* Cache the sounds in buckets on the server */
  384.  
  385.   {
  386.     Sound s;
  387.  
  388.     if ((s = SoundOpenFileForReading (sound_file))==NULL)
  389.       {
  390. #ifdef ROBUST_PLAY
  391.     signal (SIGPIPE, old_sigpipe);
  392. #endif
  393.     return 0;
  394.       }
  395.  
  396.     if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0')
  397.       {
  398.     SoundComment (s) = FileCommentFromFilename (sound_file);
  399.       }
  400.  
  401.     do_caching_play (s, volume, NULL);
  402.  
  403.     SoundCloseFile (s);
  404.  
  405.   }
  406. #endif /* CACHE_SOUNDS */
  407.  
  408. #ifndef XTEVENTS
  409.   wait_for_sounds ();
  410. #else
  411.   if (!NILP (Vsynchronous_sounds))
  412.     {
  413.       wait_for_sounds ();
  414.     }
  415. #endif
  416.  
  417. #ifdef ROBUST_PLAY
  418.   signal (SIGPIPE, old_sigpipe);
  419. #endif
  420.  
  421.   return 1;
  422. }
  423.  
  424. int
  425. play_sound_data (unsigned char *data,
  426.          int length, 
  427.          int volume)
  428. {
  429.   Sound s;
  430.   int offset;
  431.   SIGTYPE (*old_sigpipe) ();
  432.  
  433. #if !defined (XTEVENTS)
  434.   AuEvent         ev;
  435. #endif
  436.  
  437. #ifdef ROBUST_PLAY
  438.   old_sigpipe = signal (SIGPIPE, sigpipe_handle);
  439.   if (setjmp (AuXtErrorJump) !=0)
  440.     {
  441.       signal (SIGPIPE, old_sigpipe);
  442.       return 0;
  443.     }
  444. #endif
  445.  
  446.  
  447.   if (aud == NULL)
  448.     if (aud_server != NULL)
  449.       {
  450.     char *m;
  451.     /* attempt to reconect */
  452.     if ((m = init_play (aud_server)) != NULL)
  453.       {
  454. #ifdef ROBUST_PLAY
  455.         signal (SIGPIPE, old_sigpipe);
  456. #endif
  457.         return 0;
  458.       }
  459.       }
  460.     else
  461.       {
  462.     warn ("Attempt to play with no audio init\n");
  463. #ifdef ROBUST_PLAY
  464.     signal (SIGPIPE, old_sigpipe);
  465. #endif
  466.     return 0;
  467.       }
  468.  
  469.   if ((s=SoundOpenDataForReading (data, length))==NULL)
  470.     {
  471.       warn ("unknown sound type");
  472. #ifdef ROBUST_PLAY
  473.       signal (SIGPIPE, old_sigpipe);
  474. #endif
  475.       return 0;
  476.     }
  477.  
  478.   if (SoundFileFormat (s) == SoundFileFormatSnd)
  479.     {
  480.       /* hack, hack */
  481.       offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
  482.     }
  483.   else
  484.     {
  485.       warn ("only understand snd files at the moment");
  486.       SoundCloseFile (s);
  487. #ifdef ROBUST_PLAY
  488.       signal (SIGPIPE, old_sigpipe);
  489. #endif
  490.       return 0;
  491.     }
  492.  
  493. #ifndef CACHE_SOUNDS
  494.   sounds_in_play++;
  495.   AuSoundPlayFromData (aud,
  496.                s,
  497.                data+offset,
  498.                AuNone,
  499.                AuFixedPointFromFraction (volume,100),
  500.                doneCB, (AuPointer) &sounds_in_play,
  501.                NULL,
  502.                NULL,
  503.                NULL,
  504.                NULL);
  505. #else
  506.   /* Cache the sounds in buckets on the server */
  507.  
  508.   {
  509.     do_caching_play (s, volume, data+offset);
  510.   }
  511. #endif /* CACHE_SOUNDS */
  512.  
  513.  
  514. #ifndef XTEVENTS
  515.   wait_for_sounds ();
  516. #else
  517.   if (!NILP (Vsynchronous_sounds))
  518.     {
  519.       wait_for_sounds ();
  520.     }
  521. #endif
  522.  
  523.   SoundCloseFile (s); 
  524.  
  525. #ifdef ROBUST_PLAY
  526.   signal (SIGPIPE, old_sigpipe);
  527. #endif
  528.  
  529.   return 1;
  530. }
  531.  
  532. #if defined (ROBUST_PLAY)
  533.  
  534.  /********************************************************************\
  535.  *                                                                    *
  536.  * Code to protect the client from server shutdowns.                  *
  537.  *                                                                    *
  538.  * This is unbelievably horrible.                                     *
  539.  *                                                                    *
  540.  \********************************************************************/
  541.  
  542. static AuBool
  543. CatchIoErrorAndJump (AuServer *old_aud)
  544. {
  545.   if (old_aud)
  546.     warn ("Audio Server connection broken"); 
  547.   else
  548.     warn ("Audio Server connection broken because of signal");
  549.  
  550. #ifdef XTEVENTS
  551. #ifdef XTOOLKIT
  552.   {
  553.     AuXtAppRemoveAudioHandler (aud, input_id); 
  554.   }
  555. #endif
  556.  
  557.   if (aud)
  558.     AuCloseServer (aud);
  559.   aud = NULL;
  560.   sounds_in_play = 0;
  561.  
  562.   longjmp (AuXtErrorJump, 1);
  563.  
  564. #else /* not XTEVENTS */
  565.  
  566.   if (aud)
  567.     AuCloseServer (aud);
  568.   aud = NULL;
  569.   sounds_in_play = 0;
  570.   longjmp (AuXtErrorJump, 1);
  571.  
  572. #endif /* XTEVENTS */
  573. }
  574.  
  575. SIGTYPE
  576. sigpipe_handle (int signo)
  577. {
  578.   CatchIoErrorAndJump (NULL);
  579. }
  580.  
  581. static AuBool
  582. CatchErrorAndJump (AuServer *old_aud,
  583.            AuErrorEvent *event)
  584. {
  585.   return CatchIoErrorAndJump (old_aud);
  586. }
  587.  
  588. #endif /* ROBUST_PLAY */
  589.  
  590.  /********************************************************************\
  591.  *                                                                    *
  592.  * This code is here because the nas Sound library doesn't            *
  593.  * support playing from a file buffered in memory. It's a fairly      *
  594.  * direct translation of the file-based equivalent.                   *
  595.  *                                                                    *
  596.  * Since we don't have a filename, samples with no comment field      *
  597.  * are named by a section of their content.                           *
  598.  *                                                                    *
  599.  \********************************************************************/
  600.  
  601. /* Create a name from the sound. */
  602.  
  603. static char *
  604. NameFromData (CONST unsigned char *buf,
  605.           int len)
  606.  
  607. {
  608.   unsigned char name[9];
  609.   int i;
  610.   char *s;
  611.  
  612.   buf+=len/2;
  613.   len -= len/2;
  614.  
  615.   i=0;
  616.   while (i<8 && len >0)
  617.     {
  618.       while (*buf < 32 && len>0)
  619.     {
  620.       buf++;
  621.       len--;
  622.     }
  623.       name[i]= *buf;
  624.       i++;
  625.       buf++;
  626.       len--;
  627.     }
  628.  
  629.   name[i]='\0';
  630.  
  631.   if (i==8)
  632.     {
  633.       strcpy (s=malloc (10), name);
  634.     }
  635.   else 
  636.     {
  637.       strcpy (s=malloc (15), "short sound");
  638.     }
  639.  
  640.   return s;
  641. }
  642.  
  643. /* Code to do a pseudo-open on a data buffer. Only for snd files at the
  644.    moment. 
  645.  */
  646.  
  647. static SndInfo *
  648. SndOpenDataForReading (CONST char *data,
  649.                int length)
  650.  
  651. {
  652.   SndInfo        *si;
  653.   int             size;
  654.  
  655.   if (!(si = (SndInfo *) malloc (sizeof (SndInfo))))
  656.     return NULL;
  657.  
  658.   si->comment = NULL;
  659.   si->writing = 0;
  660.  
  661.   memcpy (&si->h, data, sizeof (SndHeader));
  662.  
  663.   if (LITTLE_ENDIAN)
  664.     {
  665.       char            n;
  666.     
  667.       swapl (&si->h.magic, n);
  668.       swapl (&si->h.dataOffset, n);
  669.       swapl (&si->h.dataSize, n);
  670.       swapl (&si->h.format, n);
  671.       swapl (&si->h.sampleRate, n);
  672.       swapl (&si->h.tracks, n);
  673.     }
  674.  
  675.   if (si->h.magic != SND_MAGIC_NUM)
  676.     {
  677.       free (si);
  678.       return NULL;
  679.     }
  680.  
  681.   size = si->h.dataOffset - sizeof (SndHeader);
  682.  
  683.   if (size)
  684.     {
  685.       if (!(si->comment = (char *) malloc (size + 1)))
  686.     {
  687.       free (si);
  688.       return NULL;
  689.     }
  690.  
  691.       memcpy (si->comment,  data+sizeof (SndHeader), size);
  692.  
  693.       *(si->comment + size) = 0;
  694.       if (*si->comment == '\0')
  695.     si->comment =
  696.       NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
  697.     }
  698.   else
  699.     si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
  700.  
  701.   si->h.dataSize = length-si->h.dataOffset;
  702.  
  703.   si->fp=NULL;
  704.  
  705.   return si;
  706. }
  707.  
  708. static Sound
  709. SoundOpenDataForReading (unsigned char *data,
  710.              int length)
  711.  
  712. {
  713.   Sound s;
  714.  
  715.   if (!(s = (Sound) malloc (sizeof (SoundRec))))
  716.     return NULL;
  717.  
  718.   if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
  719.     {
  720.       free (s);
  721.       return NULL;
  722.     }
  723.     
  724.  
  725.   if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
  726.     {
  727.       SndCloseFile (s->formatInfo);
  728.       free (s);
  729.       return NULL;
  730.     }
  731.  
  732.   return s;
  733. }
  734.  
  735.